﻿using System;
using System.Linq;
using System.Linq.Expressions;
using Expression_ = System.Linq.Expressions.Expression;

namespace BaseDomainNamespace.Specifications
{
    /// <summary>
    /// 仕様に別の規則を持たせる仕様を表します。
    /// </summary>
    /// <typeparam name="TEntity">エンティティの型。</typeparam>
    /// <seealso cref="Specifications.Specification&lt;TEntity&gt;"/>
    [Serializable]
    internal abstract class UnarySpecification<TEntity> : Specification<TEntity>
        where TEntity : class
    {
        /// <summary>
        /// <see cref="UnarySpecification&lt;TEntity&gt;"/> クラスの新しいインスタンスを初期化します。
        /// </summary>
        /// <param name="source">元となる仕様。</param>
        /// <exception cref="System.ArgumentNullException">source が null です。</exception>
        public UnarySpecification(Specification<TEntity> source)
        {
            if (source == null)
            {
                throw new ArgumentNullException("source");
            }
            _source = source;
        }


        /// <summary>
        /// 元となる仕様。
        /// </summary>
        private readonly Specification<TEntity> _source;


        /// <inheritdoc/>
        protected sealed override Expression<Func<TEntity, bool>> CreateExpression()
        {
            var parameter = _source.Expression.Parameters.Single();
            var sourceBody = _source.Expression.Body;
            var newBody = CreateBody(sourceBody);
            return Expression_.Lambda<Func<TEntity, bool>>(newBody, parameter);
        }

        /// <summary>
        /// 指定した仕様の式を用いる単項演算子の式を生成します。
        /// </summary>
        /// <param name="source">元となる仕様の式。</param>
        /// <returns>元となる仕様の式を用いる単項演算子の式。</returns>
        protected abstract UnaryExpression CreateBody(Expression source);
    }
}
